///////////////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2008  
// Authors: Aljosa Osep (aljosa.osep@gmail.com), Jernej Kranjec (jernej.kranjec@gmail.com)
// From Faculty of Electrical Engineering and Computer Science, Maribor, Slovenia
// Done at School of Technology, Seinajoki, Finland
// Under mentor Kimmo Salmenjoki (kimmo.salmenjoki@seamk.fi)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
///////////////////////////////////////////////////////////////////////////////////////////
//
// MainWindow.cs created with MonoDevelop
//
// To change standard headers go to Edit->Preferences->Coding->Standard Headers
//
using System;
using Gtk;
using System.Net;
using System.Net.Sockets;
using System.IO;

public partial class MainWindow: Gtk.Window
{
	//Gtk.updatec
	const int MAX_CLIENTS = 10;
	private AsyncCallback workerCallback;
	private System.Net.Sockets.Socket main_socket;
	private System.Net.Sockets.Socket [] working_sockets = new System.Net.Sockets.Socket[10];
	private int active_clients_num = 0;
	
	public int port;
	
	public MainWindow (): base (Gtk.WindowType.Toplevel)
	{
		Build ();
		readConnectionSettings(ref port);
		startListening();
	}
	
	protected void OnDeleteEvent (object sender, DeleteEventArgs a)
	{
		stopListening();
		Application.Quit ();
		a.RetVal = true;
	}
	
	private void startListening()
	{
		try
		{
			// create listening socket
			main_socket = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
			IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, port);
			main_socket.Bind(endPoint); // bind to local ip address
			main_socket.Listen(4);
			
			labelStatus.Text = "Status: listening on port " + port.ToString();
			
			// callback for other connections
			main_socket.BeginAccept(new AsyncCallback (OnClientConnection), null);
			
		}
		catch(SocketException)
		{
			Console.WriteLine("Error: ");
			labelStatus.Text = "Status: Error, cannot listen on that ip/port.";
		}
	}
	
	private void stopListening()
	{
		// close main socket
		if(main_socket != null)
		{
			main_socket.Close();
			main_socket = null;
		}
		
		// and workers
		for(int i=0; i<active_clients_num; i++)
		{
			if(working_sockets[i] != null)
			{
				working_sockets[i].Close();
				working_sockets[i] = null;
			}
		}
		
		labelStatus.Text = "Status: not listening";
	}

	
	// callback invoked when client is connected
	public void OnClientConnection(IAsyncResult asyn)
	{
		try
		{
			// complete BeginAccept() asyhronous call by calling
			// EndAccpet() which returns reference to new socket obj
			// so one instance in socket array takes the job and mainSocket
			// can listen for another connection
			working_sockets[active_clients_num] = main_socket.EndAccept(asyn);
			WaitForData(working_sockets[active_clients_num]);
			++ active_clients_num;
			
			labelStatus.Text = "Status: client " + active_clients_num.ToString() + " connected";
			
			// main socket is free and can go listen for new connection
			main_socket.BeginAccept(new AsyncCallback(OnClientConnection), null);
		}
		catch(SocketException)
		{
			// do some error message
			labelStatus.Text = "Status: problems with handling new connection.(OnClientConnection())";
		}
	}
	
	public class SocketPacket
	{
		public System.Net.Sockets.Socket m_currentSocket;
		public byte[] buffer = new byte[50];
	}
	
	public void WaitForData(System.Net.Sockets.Socket socket)
	{
		try
		{
			if(workerCallback == null)
			{
				// specify callback function to be invoked when there is
				// activity from client
				workerCallback = new AsyncCallback(OnDataReceived);
			}
			
			SocketPacket packet = new SocketPacket();
			packet.m_currentSocket = socket;
			
			// start receiving data from client asynchronously
			socket.BeginReceive(packet.buffer, 0, packet.buffer.Length, SocketFlags.None, workerCallback, packet);
		}
		catch(SocketException)
		{
			// do error message
			labelStatus.Text = "Status: problems with waiting for data. Can't receive data properly.(WaitFroData())";
		}
	}
	
	// callback function invoked when socket detects client writing of data on the stream
	public void OnDataReceived(IAsyncResult asyn)
	{
		try
		{
			SocketPacket socketData = (SocketPacket)asyn.AsyncState;
			int byte_num = 0;
			
			// complete BeginReceive() call
			// returns number of characters client has written in stream
			byte_num = socketData.m_currentSocket.EndReceive(asyn);
			char [] characters = new char[byte_num + 1];
			System.Text.Encoding.UTF8.GetDecoder().GetChars(socketData.buffer, 0, byte_num, characters, 0);
			
			// scrollbar had to been set to always wisible (possibly due to multithreading)
			
			// Gtk.Application.Invoke delegation had to be used to refresh text area widget due to multithreading
			Gtk.Application.Invoke (delegate {
				if(c_read.Active){
					textviewData.Buffer.Text = textviewData.Buffer.Text + "\n\r" + new string(characters);
				}
				else {
					textviewData.Buffer.Text = new string(characters);
				}
			});
			
			// continue waiting for data on this socker
			WaitForData(socketData.m_currentSocket);
		}
		catch(SocketException)
		{
			// error msg
			labelStatus.Text = "Status: problem with assembling data(OnDataReceived())";
		}
	}
	
	// read port setting from file
	public void readConnectionSettings(ref int _port)
    {
        StreamReader config; // stream instance
        try
        {
            config = File.OpenText("server_config.cfg");  // open config
            _port = System.Convert.ToInt16(config.ReadLine()); // read first line
            config.Close(); // close file
        }
        catch (FileNotFoundException)  // file not found - set defaults
        {
       //     MessageBox.Show("Error opnening config, it may be missing. Setting default settings: 192.168.0.1:80");
            _port = 8000; // set default port
        }
    }
	
	
	/*************************************
	 * Button callbacks
	 * ***********************************/	
	// button start listening - run listening function
/*	protected virtual void OnButtonStartClicked (object sender, System.EventArgs e)
	{
		startListening();
	}

	protected virtual void OnStopListeningClicked (object sender, System.EventArgs e)
	{
		stopListening();	
	} */

	/*************************************
	 * Menu callbacks
	 * ***********************************/
	// show connection dialog
	protected virtual void OnMenuConnectionClick (object sender, System.EventArgs e)
	{
		simpleServer.connection connectionDialog = new simpleServer.connection(this); // make instance, pass this class as parameter
		ResponseType response = (ResponseType)connectionDialog.Run();
		
		if (response == ResponseType.Ok)
		{
			connectionDialog.saveConfiguration();  // if response is ok, then save configuration
		}
		
		connectionDialog.Destroy();  // destroy dialog since we don't need it anymore
	}

	// run listening function
	protected virtual void onMenuStartListening (object sender, System.EventArgs e)
	{
		startListening();
	}
	
	// run stop listening 
	protected virtual void OnMenuStopListening (object sender, System.EventArgs e)
	{
		stopListening();
	}

	// show about dialog
	protected virtual void OnMenuAbout (object sender, System.EventArgs e)
	{
		simpleServer.about aboutDialog = new simpleServer.about();
		ResponseType response = (ResponseType)aboutDialog.Run();
		
		if (response == ResponseType.Ok)
		{
			aboutDialog.Destroy();  // destroy dialog since we don't need it anymore
		}
	}

	protected virtual void OnMenuQuit (object sender, System.EventArgs e)
	{
		Application.Quit();
	}
}